﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Pfz.ErrorHandling
{
	/// <summary>
	/// Class used to repack errors under another error.
	/// In general, it is used to link errors to properties and records, but you
	/// can inherit it to create other types of repacking.
	/// </summary>
	public abstract class ErrorRepackBase:
		IDisposable
	{
		private readonly object _key;
		private readonly _ErrorPack _errorPack;
		private readonly ErrorRepackBase _parent;
		internal readonly List<object> _errors = new List<object>();
		internal readonly Dictionary<object, ErrorRepackBase> _subPacks = new Dictionary<object, ErrorRepackBase>();
		internal readonly ReadOnlyCollection<object> _roErrors;
	
		/// <summary>
		/// Initializes the repacking.
		/// </summary>
		public ErrorRepackBase(object key)
		{
			_key = key;
			var errorPack = ErrorPack._current;
			
			if (errorPack != null)
			{
				_parent = errorPack._actualErrorPack;
				
				ErrorRepackBase old;
				_parent._subPacks.TryGetValue(_key, out old);
				if (old != null)
					errorPack._actualErrorPack = old;
				else
				{
					_parent._subPacks.Add(key, this);
					errorPack._actualErrorPack = this;
				}
			}
			else
			{
				errorPack = new _ErrorPack(this);
				ErrorPack._current = errorPack;
				errorPack._actualErrorPack = this;
			}

			_errorPack = errorPack;
			_roErrors = new ReadOnlyCollection<object>(_errors);
		}
		
		/// <summary>
		/// Finishes this object and repack any errors if available.
		/// </summary>
		public void Dispose()
		{
			if (!_errorPack.Dispose(this))
				_errorPack._actualErrorPack = _parent;
		}
		
		/// <summary>
		/// Must be implemented to create new error packs.
		/// </summary>
		protected abstract object CreatePack(ReadOnlyCollection<object> errors);

		internal void Add(object error)
		{
			_errors.Add(error);
			
			if (_parent != null && _errors.Count == 1)
			{
				var repack = CreatePack(_roErrors);
				_parent.Add(repack);
			}
		}
	}
}
